#include <ObjectSubSystem/ObjectSubSystem.h>
#include <ObjectSubSystem/EmptyObject.h>
#include <Math/MathConstants.h>
#include <Math/Bitfield.h>
#include <boost/pool/object_pool.hpp>

//file:class.cpp
cvPool EmptyObject::pool(sizeof(EmptyObject));

EmptyObject * EmptyObject::create()	{ return new EmptyObject(); }
void * EmptyObject::operator new(size_t size)	{ return pool.malloc(); }

EmptyObject::EmptyObject() :
	fn_updater(NULL),
	fn_position(&EmptyObject::position_glb),
	fn_rotate(&EmptyObject::rotate_glb),
	fn_move(&EmptyObject::move_glb),
	fn_turn(&EmptyObject::turn_glb),
	fn_translate(&EmptyObject::translate_glb),
	m_localpos(0,0),
	m_localrot(0.0f),
	m_position(0,0),
	m_rotation(0) {}

EmptyObject * EmptyObject::copy(cvMath::bitfield_t flags)
{
	EmptyObject * obj = ObjectSubSystem::getInstance()->create<EmptyObject>();
	// TODO flags are not used..
	//obj->BaseObject::setParent(getParent());		// clone parenting data?
	obj->m_relativityflags		= m_relativityflags;
	obj->m_position		= m_position;
	obj->m_rotation		= m_rotation;
	obj->m_localpos		= m_localpos;
	obj->m_localrot		= m_localrot;
	return obj;
}

// Object destruction:
void EmptyObject::destroy()	{ delete this; }
void EmptyObject::operator delete(void * ptr)	{ pool.free(ptr); }
void EmptyObject::purgeMemoryPool()	{ pool.purge_memory(); }


//file:relativity.cpp
int EmptyObject::objectType() const
{
	return GAMEOBJECT_EMPTY;
}

void EmptyObject::setRelativity(cvMath::bitfield_t flags)
{
	if(flags == OBJ_DEFAULT)	{ flags = OBJ_XYROT; }

	if(m_relativityflags != flags)
	{   // check parent compability with this type.
		if(getParent()->objectType() != GAMEOBJECT_BASE)
		{
			// Dispatch flags.
			switch(cvMath::filterFlags(flags, OBJ_XYROT))
			{
				case OBJ_XYROT:	fn_position = &EmptyObject::position_local;
								fn_move = &EmptyObject::move_local;
								fn_translate = &EmptyObject::translate_local;
								fn_rotate = &EmptyObject::rotate_local;
								fn_turn = &EmptyObject::turn_local;
								fn_updater = &EmptyObject::xyrot_update;
								break;
				case OBJ_XY:	fn_position = &EmptyObject::position_local;
								fn_move = &EmptyObject::move_local;
								fn_translate = &EmptyObject::translate_local;
								fn_rotate = &EmptyObject::rotate_glb;
								fn_turn = &EmptyObject::turn_glb;
								fn_updater = &EmptyObject::xy_update;
								break;
				case OBJ_X:		fn_position = &EmptyObject::position_localx;
								fn_move = &EmptyObject::move_localx;
								fn_translate = &EmptyObject::translate_localx;
								fn_rotate = &EmptyObject::rotate_glb;
								fn_turn = &EmptyObject::turn_glb;
								fn_updater = &EmptyObject::x_update;
								break;
				case OBJ_Y:		fn_position = &EmptyObject::position_localy;
								fn_move = &EmptyObject::move_localy;
								fn_translate = &EmptyObject::translate_localy;
								fn_rotate = &EmptyObject::rotate_glb;
								fn_turn = &EmptyObject::turn_glb;
								fn_updater = &EmptyObject::y_update;
								break;
				case OBJ_ROTATION:	fn_position = &EmptyObject::position_glb;
								fn_move = &EmptyObject::move_glb;
								fn_translate = &EmptyObject::translate_glb;
								fn_rotate = &EmptyObject::rotate_local;
								fn_turn = &EmptyObject::turn_local;
								fn_updater = &EmptyObject::rot_update;
								break;
				default:		fn_position = &EmptyObject::position_glb;	// <no relativity>
								fn_move = &EmptyObject::move_glb;
								fn_translate = &EmptyObject::translate_glb;
								fn_rotate = &EmptyObject::rotate_glb;
								fn_turn = &EmptyObject::turn_glb;
								fn_updater = NULL;
			}
			cvMath::setFlag_on(m_relativityflags, cvMath::filterFlags(flags, OBJ_XYROT));
		}
	}

	BaseObject::setRelativity(flags);
}

//file:update.cpp
// The main updater host
void EmptyObject::updater()
{
	// pull BaseObject attributes from parent
	BaseObject::updater();
	// If possible, pull EmptyObject attributes from parent.
	if(fn_updater)
	{
		(this->*fn_updater)();
	}
}

// Relativity combinations:
void EmptyObject::xyrot_update()	// pointted
{
	const EmptyObject* obj = static_cast<EmptyObject*>(getParent());
	float _cos, _sin;
	m_rotation = obj->m_rotation + m_localrot;
	_cos = cosf(-m_rotation);
	_sin = sinf(-m_rotation);
	m_position.x = obj->m_position.x + ( _cos * m_localpos.x + _sin * m_localpos.y);
	m_position.y = obj->m_position.y + (-_sin * m_localpos.x + _cos * m_localpos.y);
	cvMath::setFlag_on(m_validrelatives, OBJ_XYROT);
}
void EmptyObject::xy_update()	// translated
{
	m_position = static_cast<EmptyObject*>(getParent())->m_position + m_localpos;
	cvMath::setFlag_on(m_validrelatives, OBJ_XY);
}
// separate attributes:
void EmptyObject::rot_update()  //rotated
{
	m_rotation = static_cast<EmptyObject*>(getParent())->m_rotation + m_localrot;
	cvMath::setFlag_on(m_validrelatives, OBJ_ROTATION);
}
void EmptyObject::x_update()    //x-translated
{
	m_position.x = static_cast<EmptyObject*>(getParent())->m_position.x + m_localpos.x;
	cvMath::setFlag_on(m_validrelatives, OBJ_X);
}
void EmptyObject::y_update()    //y-translated
{
	m_position.y = static_cast<EmptyObject*>(getParent())->m_position.y + m_localpos.y;
	cvMath::setFlag_on(m_validrelatives, OBJ_Y);
}


//file:position.cpp
/// Setters, see EmptyObject::setRelativity()
void EmptyObject::position(const cvMath::Vec2f &p)			{ (this->*fn_position)(p); }	// *public method*
void EmptyObject::position_glb(const cvMath::Vec2f &p)		{ m_position = p; }
void EmptyObject::position_local(const cvMath::Vec2f &p)	{ m_localpos = p; cvMath::setFlag_off(m_validrelatives, OBJ_XY); }
void EmptyObject::position_localx(const cvMath::Vec2f &p)
{
	m_localpos.x = p.x;
	m_position.y = p.y;
	cvMath::setFlag_off(m_validrelatives, OBJ_X);
}
void EmptyObject::position_localy(const cvMath::Vec2f &p)
{
	m_position.x = p.x;
	m_localpos.y = p.y;
	cvMath::setFlag_off(m_validrelatives, OBJ_Y);
}

//file:angle.cpp >>>>
void EmptyObject::rotate(float angle)		{ (this->*fn_rotate)(angle * DEGREES_TO_RAD); }	// *public method*
void EmptyObject::rotate_glb(float angle)	{ m_rotation = angle; }
void EmptyObject::rotate_local(float angle)	{ m_localrot = angle; cvMath::setFlag_off(m_validrelatives, OBJ_ROTATION); }
//<<<
void EmptyObject::move(float fwrd, float sidewrd)	{ (this->*fn_move)(fwrd, sidewrd); }	// *public method*
void EmptyObject::move_glb(float fwrd, float sidewrd)
{
	float _cos = cos(-m_rotation);
	float _sin = sin(-m_rotation);
	m_position.x += _cos * fwrd + _sin * sidewrd;
	m_position.y += -_sin * fwrd + _cos * sidewrd;
}

void EmptyObject::move_local(float fwrd, float sidewrd)
{
	float _cos = cos(-m_rotation);
	float _sin = sin(-m_rotation);
	m_localpos.x += _cos * fwrd + _sin * sidewrd;
	m_localpos.y += -_sin * fwrd + _cos * sidewrd;
	cvMath::setFlag_off(m_validrelatives, OBJ_XY);
}

void EmptyObject::move_localx(float fwrd, float sidewrd)
{
	float _cos = cos(-m_rotation);
	float _sin = sin(-m_rotation);
	m_localpos.x += _cos * fwrd + _sin * sidewrd;
	m_position.y += -_sin * fwrd + _cos * sidewrd;
	cvMath::setFlag_off(m_validrelatives, OBJ_X);
}

void EmptyObject::move_localy(float fwrd, float sidewrd)
{
	float _cos = cos(-m_rotation);
	float _sin = sin(-m_rotation);
	m_position.x += _cos * fwrd + _sin * sidewrd;
	m_localpos.y += -_sin * fwrd + _cos * sidewrd;
	cvMath::setFlag_off(m_validrelatives, OBJ_X);
}
//file:angle.cpp >>>>
void EmptyObject::turn(float delta)		{ (this->*fn_turn)(delta * DEGREES_TO_RAD); }	// *public method*
void EmptyObject::turn_glb(float delta)	{ m_rotation += delta; }
void EmptyObject::turn_local(float delta)	{ m_localrot += delta; cvMath::setFlag_off(m_validrelatives, OBJ_ROTATION); }
//<<<
void EmptyObject::translate(const cvMath::Vec2f &p)			{ (this->*fn_translate)(p); }	// *public method*
void EmptyObject::translate_glb(const cvMath::Vec2f &p)		{ m_position += p; }
void EmptyObject::translate_local(const cvMath::Vec2f &p)	{ m_localpos += p; cvMath::setFlag_off(m_validrelatives, OBJ_XY); }
void EmptyObject::translate_localx(const cvMath::Vec2f &p)
{
	m_localpos.x += p.x;
	m_position.y += p.y;
	cvMath::setFlag_off(m_validrelatives, OBJ_X);
}
void EmptyObject::translate_localy(const cvMath::Vec2f &p)
{
	m_position.x += p.x;
	m_localpos.y += p.y;
	cvMath::setFlag_off(m_validrelatives, OBJ_Y);
}

/// Getters
const cvMath::Vec2f& EmptyObject::position()
{
	// We must do recurssive update on the objects position
	update_linear();
	return m_position;
}

//file:angle.cpp
const float EmptyObject::angle()
{
	update_linear();
	return m_rotation * RAD_TO_DEGREES;
}

const cvMath::Vec2f& EmptyObject::localPosition() const	{ return m_localpos; }

const float EmptyObject::localAngle() const	{ return m_localrot; }
